package app;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.durcframework.open.OpenUtil;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration(value = "src/main/webapp")
@ContextHierarchy({
		@ContextConfiguration(name = "parent", locations = "classpath:applicationContext.xml"),
		@ContextConfiguration(name = "child", locations = "classpath:springMVC.xml") })
public class ApiTestBase {
	
	@Autowired
	private WebApplicationContext wac;
	private MockMvc mockMvc;
	
	// config
	private String appId = "test";
	private String secret = "123456";

	private String appIdName = "appId";
	private String signName = "sign";
	private String timestampName = "timestamp";

	@Before
	public void setUp() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}
	
	public MockHttpServletRequestBuilder buildGetReq4Json(String urlTemplate, Object... urlVariables) {
		return this.buildGetReq(urlTemplate,MediaType.APPLICATION_JSON);
	}
	
	public MockHttpServletRequestBuilder buildPostReq4Json(String urlTemplate, Object... urlVariables) {
		return this.buildPostReq(urlTemplate,MediaType.APPLICATION_JSON);
	}
	
	public MockHttpServletRequestBuilder buildGetReq4Xml(String urlTemplate, Object... urlVariables) {
		return this.buildGetReq(urlTemplate,MediaType.APPLICATION_XML);
	}
	
	public MockHttpServletRequestBuilder buildPostReq4Xml(String urlTemplate, Object... urlVariables) {
		return this.buildPostReq(urlTemplate,MediaType.APPLICATION_XML);
	}
	
	public MockHttpServletRequestBuilder buildGetReq(String urlTemplate,MediaType... mediaTypes) {
		return MockMvcRequestBuilders.get(urlTemplate).accept(mediaTypes);
	}
	
	public MockHttpServletRequestBuilder buildPostReq(String urlTemplate,MediaType... mediaTypes) {
		return MockMvcRequestBuilders.post(urlTemplate).accept(mediaTypes);
	}
	
	/**
	 * 发送POST请求
	 * @param url 请求连接
	 * @param params 请求参数
	 * @return 服务端返回的内容
	 */
	public MvcResult postJSON(String url,Map<String, String> params) {
		return this.doReq(url, params, new ReqFactory() {
			@Override
			public MockHttpServletRequestBuilder buildReq(String url) {
				return buildPostReq4Json(url);
			}
		});
	}
	
	/**
	 * 发送GET请求
	 * @param url 请求连接
	 * @param params 请求参数
	 * @return 服务端返回的内容
	 */
	public MvcResult getJSON(String url,Map<String, String> params) {
		return this.doReq(url, params, new ReqFactory() {
			@Override
			public MockHttpServletRequestBuilder buildReq(String url) {
				return buildGetReq4Json(url);
			}
		});
	}
	
	/**
	 * 发送请求
	 * @param url 请求连接
	 * @param params 请求参数
	 * @return 服务端返回的内容
	 */
	public MvcResult postXML(String url,Map<String, String> params) {
		return this.doReq(url, params, new ReqFactory() {
			@Override
			public MockHttpServletRequestBuilder buildReq(String url) {
				return buildPostReq4Xml(url);
			}
		});
	}
	
	public MvcResult getXML(String url,Map<String, String> params) {
		return this.doReq(url, params, new ReqFactory() {
			@Override
			public MockHttpServletRequestBuilder buildReq(String url) {
				return buildGetReq4Xml(url);
			}
		});
	}
	
	public MvcResult doReq(String url,Map<String, String> params,ReqFactory reqFactory) {
		params = this.buildReqParams(params);
		
		MockHttpServletRequestBuilder req = reqFactory.buildReq(url);
		
		Set<String> paramKeys = params.keySet();
		for (String paramName : paramKeys) {
			req.param(paramName, params.get(paramName));
		}
		
		try {
			return this.getMockMvc().perform(req).andDo(MockMvcResultHandlers.print())  
			        .andReturn();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		} 
	}
	
	interface ReqFactory {
		MockHttpServletRequestBuilder buildReq(String url);
	}
	
	private Map<String, String> buildReqParams(Map<String, String> params) {
		params.put(appIdName, this.appId);
		params.put(timestampName, String.valueOf(System.currentTimeMillis()));
		
		String sign = null;
		Map<String, String> checkSignParam = this.buildCheckSignParam();
		try {
			sign = OpenUtil.buildSign(checkSignParam, this.secret);
		} catch (IOException e1) {
			e1.printStackTrace();
			throw new RuntimeException("签名构建失败");
		}
		
		params.put(signName, sign);
		
		return params;
	}
	
	private Map<String,String> buildCheckSignParam() {
		Map<String, String> checkSignParam = new HashMap<String, String>(2);
		checkSignParam.put(appIdName, appId);
		checkSignParam.put(timestampName, String.valueOf(System.currentTimeMillis()));
		return checkSignParam;
	}

	public WebApplicationContext getWac() {
		return wac;
	}

	public void setWac(WebApplicationContext wac) {
		this.wac = wac;
	}

	public MockMvc getMockMvc() {
		return mockMvc;
	}

	public void setMockMvc(MockMvc mockMvc) {
		this.mockMvc = mockMvc;
	}

}
